﻿//----------------------------------------------------------------
// Copyright (c) 2011-2013 
// All rights reserved.
// Author: Ben DaoWei 
// Contact: bendaowei@gmail.com
// Web Link: http://xnaeditor.codeplex.com
// ----------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using UnityEngine.IO;
using Microsoft.Xna.Framework;
using UnityEngine.Components;
using UnityEngine.Core;
using UnityEngine.Terrain;
using UnityEngine.Graphics;
using System.Diagnostics;
using Microsoft.Xna.Framework.Graphics;
using UnityEngine.Graphics.Texture;
using UnityEngine.Primitive;
using UnityEngine.Gameplay;
using UnityEngine.Gameplay.Match;

namespace UnityEngine.Terrain
{
    public class WarCraftEnvironment
    {
        public Landscape Landscape
        {
            get;
            set;
        }

        public WarCraftEnvironment(Landscape owner, WCLoader loader)
        {
            this.Landscape = owner;

            loader.ExpectTag("W3E!"); // Expected header
            Version = loader.ReadInt32(); // Version 11
            TextureSet = (char)loader.ReadByte(); // Felwood texture set, texture set are set in landscape
            UseCustomTexture = (loader.ReadInt32() == 1); // use custome tileset

            TextureNames = new string[loader.ReadInt32()];
            for (int i = 0; i < TextureNames.Length; i++)
                TextureNames[i] = loader.ReadString(4);

            CliffNames = new string[loader.ReadInt32()];
            for (int i = 0; i < CliffNames.Length; i++)
                CliffNames[i] = loader.ReadString(4);

            Width = loader.ReadInt32();
            Height = loader.ReadInt32();

            CenterOffsetX = loader.ReadFloat();
            CenterOffsetY = loader.ReadFloat();

            this.VertexInfos = new VertexInfo[Width * Height];
            for (int x = 0; x < Width; x++)
                for (int y = 0; y < Height; y++)
                {
                    VertexInfos[x + y * Width] = new VertexInfo(loader);
                    VertexInfos[x + y * Width].Position.X = x * GridSize;
                    VertexInfos[x + y * Width].Position.Y =
                        (VertexInfos[x + y * Width].Position.Y - 0x2000 + (VertexInfos[x + y * Width].LayerHeight - 2) * 0x0200) / 4;
                    VertexInfos[x + y * Width].Position.Z = y * GridSize;
                }
        }

        public float GetLightValue(Vector3 position)
        {
            float retValue = 0.3f;
            int pX = UnityHelper.GetFloorValue(position.X / 128.0f);
            int pY = UnityHelper.GetFloorValue(position.Z / 128.0f);

            if (pX < 0 || pX + 1 >= this.Width || pY < 0 || pY + 1 >= this.Width)
                return retValue;

            VertexInfo A = this[pX, pY];
            VertexInfo B = this[pX + 1, pY];
            VertexInfo D = this[pX + 1, pY + 1];
            VertexInfo C = this[pX, pY + 1];

            float LightA = this._LightInfo[pX, pY];
            float LightB = this._LightInfo[pX + 1, pY];
            float LightC = this._LightInfo[pX + 1, pY + 1];
            float LightD = this._LightInfo[pX, pY + 1];

            string name;
            ECellType cellType = CliffPaletteRendererContainer.GetCellType(A, B, D, C, out name);

            if (true || cellType == ECellType.Surface || cellType == ECellType.Ramp)
            {
                // if in the triangle A - B - C
                if (position.X - A.Position.X + position.Z - A.Position.Z <= 128.0f)
                {
                    float h1 = (position.X - A.Position.X) / (B.Position.X - A.Position.X) * (LightB - LightA);
                    float h2 = (position.Z - A.Position.Z) / (C.Position.Z - A.Position.Z) * (LightC - LightA);

                    retValue = h1 + h2 + LightA;
                }
                else
                {
                    // in the triangle D - C - B
                    float h1 = (position.X - D.Position.X) / (C.Position.X - D.Position.X) * (LightC - LightD);
                    float h2 = (position.Z - D.Position.Z) / (B.Position.Z - D.Position.Z) * (LightB - LightD);

                    retValue = h1 + h2 + LightD;
                }
            }
            else
            {
                Debug.WriteLine("Place object on cliff/clifftrans is not supported");
                //throw new Exception("Place object on cliff/clifftrans is not supported.");
            }

            return retValue;
        }

        public void CalculateLight(AreaLightObject[] lights, int LightCount, GameTime time)
        {
            // Step 1: Create light info storage.
            if (this._LightInfo == null)
                this._LightInfo = new float[this.Width, this.Height];
            if (this._TargetLightInfo == null)
                this._TargetLightInfo = new float[this.Width, this.Height];
            //if (this._LightBlocker == null)
            //    this._LightBlocker = new int[4, BlockSize];

            // Step 2: Reset light infos
            for (int i = 0; i < this.Width; i++)
                for (int j = 0; j < this.Height; j++)
                    this._TargetLightInfo[i, j] = AreaLightObject.DEFAULT_LIGHT;

            // Step 3: Calculate light infos
            for (int lightIndex = 0; lightIndex < LightCount; lightIndex++)
            {
                AreaLightObject light = lights[lightIndex];

                bool pass = false;
                if (light.GameObject.GetParent<Team>().TeamRelationType == TeamRelationType.Self ||
                    light.GameObject.GetParent<Team>().TeamRelationType == TeamRelationType.Neutral &&
                    light.GameObject.Name.Contains("HeroStore"))
                    pass = true;
                if (pass == false)
                    continue;
                //int x, y;

                //for (int i = 0; i < 4; i++)
                //    for (int j = 0; j < this.BlockSize; j++)
                //        this._LightBlocker[i, j] = 0;

                //this._TargetLightInfo[sx >> 2, sy >> 2] = 1.0f;
                light.CalculateLights();
                int sx = UnityHelper.GetFloorValue(light.Position.X / 32.0f);
                int sy = UnityHelper.GetFloorValue(light.Position.Z / 32.0f);
                int mx = 4 - (sx - light.Width) % 4;
                int my = 4 - (sy - light.Width) % 4;
                for (int i = -light.Width + mx; i <= light.Width; i += 4)
                    for (int j = -light.Width + my; j <= light.Width; j += 4)
                        if (i + light.Width >= 0 && i + light.Width < light.Lighted.GetLength(0) &&
                            j + light.Width >= 0 && j + light.Width < light.Lighted.GetLength(1) &&
                            light.Lighted[i + light.Width, j + light.Width])
                        {
                            Debug.Assert((sx + i) % 4 == 0);
                            Debug.Assert((sy + j) % 4 == 0);
                            int tx = (sx + i) >> 2;
                            int ty = (sy + j) >> 2;
                            if (tx < 0 || ty < 0 || tx >= this._TargetLightInfo.GetLength(0) || ty >= this._TargetLightInfo.GetLength(1))
                                continue;
                            this._TargetLightInfo[tx, ty] = 1.0f;
                        }
            }

            // Step 4: smooth light
            float[,] temp = new float[_TargetLightInfo.GetLength(0), _TargetLightInfo.GetLength(1)];
            for (int i = 0; i < this.Width; i++)
                for (int j = 0; j < this.Height; j++)
                {
                    float all = 0;
                    float num = 0;
                    for (int x = -1; x < 2; x++)
                        for (int y = -1; y < 2; y++)
                        {
                            if (i + x < 0 || i + x >= this.Width ||
                                j + y < 0 || j + y >= this.Height)
                                continue;
                            all = all + _TargetLightInfo[i + x, j + y];
                            num = num + 1;
                        }
                    temp[i, j] = all / num;
                }
            for (int i = 0; i < this.Width; i++)
                for (int j = 0; j < this.Height; j++)
                    _TargetLightInfo[i, j] = temp[i, j];

            // Step 5: Change to target light
            float seconds = (float)time.ElapsedGameTime.TotalSeconds;
            for (int i = 0; i < this.Width; i++)
                for (int j = 0; j < this.Height; j++)
                {
                    float differ = _TargetLightInfo[i, j] - _LightInfo[i, j];
                    if (Math.Abs(differ) > seconds)
                        _LightInfo[i, j] += differ < 0 ? -seconds : seconds;
                    else
                        _LightInfo[i, j] = _TargetLightInfo[i, j];
                }
        }

        public void GenerateGraphics()
        {
            // Recalculate heights for ramps
            CalculateRampsHeight();
            GenerateCliffs(this.Landscape.WarCraftEnvironment.Width - 1, this.Landscape.WarCraftEnvironment.Height - 1);

            // Generate each Resolution * Resolution terrain cell
            int Resolution = 16;
            VertexPositionNormalTextureFogBones[] container = new VertexPositionNormalTextureFogBones[(Resolution + 1) * (Resolution + 1)];
            VertexInfo[,] VertexInfos = new VertexInfo[(Resolution + 1), (Resolution + 1)];
            for (int x = 0; x < this.Landscape.WarCraftEnvironment.Width - 1; x += Resolution)
                for (int y = 0; y < this.Landscape.WarCraftEnvironment.Height - 1; y += Resolution)
                {
                    for (int i = 0; i < Resolution + 1; i++)
                        for (int j = 0; j < Resolution + 1; j++)
                        {
                            int index = i + j * (Resolution + 1);
                            container[index] = GenerateTerrainVertex(x + i, y + j);
                            container[index].TextureCoordinate = new Vector2((float)i / (float)(Resolution), (float)j / (float)(Resolution));
                            VertexInfos[i, j] = this.Landscape.WarCraftEnvironment.VertexInfos[Index(x + i, y + j)];
                        }

                    LandscapeCellRenderer renderer = new LandscapeCellRenderer(this.Landscape, Resolution, container, VertexInfos);
                    LandscapeCellInstance Instance = new LandscapeCellInstance(renderer);
                    SceneManager.Instance.AddInstance(Instance);
                }
        }

        private void GenerateCliffs(int width, int height)
        {
            VertexInfo[,] VertexInfos = new VertexInfo[width + 1, height + 1];
            for (int i = 0; i < width + 1; i++)
                for (int j = 0; j < height + 1; j++)
                {
                    VertexInfos[i, j] = this.Landscape.WarCraftEnvironment.VertexInfos[Index(i, j)];
                }

            Debug.Assert(VertexInfos.GetLength(0) == width + 1);
            Debug.Assert(VertexInfos.GetLength(1) == height + 1);

            //_PaletteInstances = new List<PaletteInstance>();

            // 1-->3
            // |   |
            // 0-->2
            // 0 -> 1 -> 3, 0 -> 3 -> 2
            List<int> Triangles = new List<int>();
            for (int i = 0; i < width; i++)
                for (int j = 0; j < height; j++)
                {
                    string name;
                    ECellType cellType = CliffPaletteRendererContainer.GetCellType(
                        VertexInfos[i, j],
                        VertexInfos[i + 1, j],
                        VertexInfos[i + 1, j + 1],
                        VertexInfos[i, j + 1],
                        out name);

                    PaletteRendererManager manager = SceneManager.Instance.PaletteRendererManager;
                    if (cellType == ECellType.Cliff)
                    {
                        // position
                        Vector3 position = new Vector3(VertexInfos[i, j + 1].Position.X, 0, VertexInfos[i, j + 1].Position.Z);
                        position.Y = Math.Min(Math.Min(VertexInfos[i, j].Position.Y, VertexInfos[i + 1, j].Position.Y),
                                              Math.Min(VertexInfos[i, j + 1].Position.Y, VertexInfos[i + 1, j + 1].Position.Y));

                        // rotation & states
                        Vector3 rotation = new Vector3(0, -(float)Math.PI / 2.0f, -(float)Math.PI / 2.0f);
                        ERenderState states = ERenderState.Opaque | ERenderState.Solid | ERenderState.CullCounterClockWise | ERenderState.EnableDepth;

                        Texture2D cliffTexture = (VertexInfos[i, j].CliffTextureType == 1) ? TextureManager.Instance.Cliff0 : TextureManager.Instance.Cliff1;
                        int textureId = (VertexInfos[i, j].CliffTextureType == 1) ? 0 : 3;
                        PaletteInstance instance = new CliffInstance(VertexInfos[i, j].Position, VertexInfos[i + 1, j].Position,
                                                                     VertexInfos[i + 1, j + 1].Position, VertexInfos[i, j + 1].Position,
                                                                     new Vector3(), new Vector3(), false, EDirection.None, ESide.None,
                                                                     cliffTexture, position, rotation, states, manager.GetPaletteRenderer(EPaletteRendererType.Cliff, name) as CliffRenderer);
                        SceneManager.Instance.AddInstance(instance);
                        // Cliff changes tile texture around
                        VertexInfos[i, j].TextureId = VertexInfos[i + 1, j].TextureId = VertexInfos[i, j + 1].TextureId = VertexInfos[i + 1, j + 1].TextureId = (byte)textureId;
                    }

                    if (cellType == ECellType.CliffTrans)
                    {
                        const int DefaultGridSize = 128;

                        ESide side = ESide.None;

                        // it's cliff ramp, so it must have a direction, from higher layer to lower layer, and a side, left or right
                        EDirection direction = CliffPaletteRendererContainer.GetDirection(
                            VertexInfos[i, j].LayerHeight, VertexInfos[i, j].IsRampPoint,
                            VertexInfos[i + 1, j].LayerHeight, VertexInfos[i + 1, j].IsRampPoint,
                            VertexInfos[i + 1, j + 1].LayerHeight, VertexInfos[i + 1, j + 1].IsRampPoint,
                            VertexInfos[i, j + 1].LayerHeight, VertexInfos[i, j + 1].IsRampPoint, ref side);


                        // Set surface invisible
                        VertexInfos[i, j].SurfaceVisible[0] = false;
                        VertexInfos[i + 1, j].SurfaceVisible[1] = false;
                        VertexInfos[i + 1, j + 1].SurfaceVisible[2] = false;
                        VertexInfos[i, j + 1].SurfaceVisible[3] = false;

                        //DebugPrimitiveRenderer.AddPermanentPrimitive(VertexInfos[i, j].Position, Color.Red);
                        //DebugPrimitiveRenderer.AddPermanentPrimitive(VertexInfos[i + 1, j].Position, Color.Red);
                        //DebugPrimitiveRenderer.AddPermanentPrimitive(VertexInfos[i, j + 1].Position, Color.Red);
                        //DebugPrimitiveRenderer.AddPermanentPrimitive(VertexInfos[i + 1, j + 1].Position, Color.Red);

                        switch (direction)
                        {
                            case EDirection.North:
                                VertexInfos[i + 1, j].SurfaceVisible[0] = false;
                                VertexInfos[i + 2, j].SurfaceVisible[1] = false;
                                VertexInfos[i + 2, j + 1].SurfaceVisible[2] = false;
                                VertexInfos[i + 1, j + 1].SurfaceVisible[3] = false;
                                //DebugPrimitiveRenderer.AddPermanentPrimitive(VertexInfos[i + 2, j].Position, Color.Purple);
                                //DebugPrimitiveRenderer.AddPermanentPrimitive(VertexInfos[i + 2, j + 1].Position, Color.Purple);
                                break;
                            case EDirection.South:
                                VertexInfos[i - 1, j].SurfaceVisible[0] = false;
                                VertexInfos[i, j].SurfaceVisible[1] = false;
                                VertexInfos[i, j + 1].SurfaceVisible[2] = false;
                                VertexInfos[i - 1, j + 1].SurfaceVisible[3] = false;
                                //DebugPrimitiveRenderer.AddPermanentPrimitive(VertexInfos[i - 1, j].Position, Color.Purple);
                                //DebugPrimitiveRenderer.AddPermanentPrimitive(VertexInfos[i - 1, j + 1].Position, Color.Purple);
                                break;
                            case EDirection.West:
                                VertexInfos[i, j - 1].SurfaceVisible[0] = false;
                                VertexInfos[i + 1, j - 1].SurfaceVisible[1] = false;
                                VertexInfos[i + 1, j].SurfaceVisible[2] = false;
                                VertexInfos[i, j].SurfaceVisible[3] = false;
                                //DebugPrimitiveRenderer.AddPermanentPrimitive(VertexInfos[i + 1, j - 1].Position, Color.Purple);
                                //DebugPrimitiveRenderer.AddPermanentPrimitive(VertexInfos[i, j - 1].Position, Color.Purple);
                                break;
                            case EDirection.East:
                                VertexInfos[i, j + 1].SurfaceVisible[0] = false;
                                VertexInfos[i + 1, j + 1].SurfaceVisible[1] = false;
                                VertexInfos[i + 1, j + 2].SurfaceVisible[2] = false;
                                VertexInfos[i, j + 2].SurfaceVisible[3] = false;
                                //DebugPrimitiveRenderer.AddPermanentPrimitive(VertexInfos[i + 1, j + 2].Position, Color.Purple);
                                //DebugPrimitiveRenderer.AddPermanentPrimitive(VertexInfos[i, j + 2].Position, Color.Purple);
                                break;
                        }


                        Vector3 LeftBottom = VertexInfos[i, j].Position;
                        Vector3 LeftTop = VertexInfos[i + 1, j].Position;
                        Vector3 RightTop = VertexInfos[i + 1, j + 1].Position;
                        Vector3 RightBottom = VertexInfos[i, j + 1].Position;

                        Vector3 NoMove = new Vector3(0, 0, 0);
                        Vector3 MoveUp = new Vector3(DefaultGridSize, 0, 0);
                        Vector3 MoveDown = new Vector3(-DefaultGridSize, 0, 0);
                        Vector3 MoveLeft = new Vector3(0, 0, -DefaultGridSize);
                        Vector3 MoveRight = new Vector3(0, 0, DefaultGridSize);

                        Vector3 LeftMiddle = new Vector3();
                        Vector3 RightMiddle = new Vector3();

                        // Position
                        Vector3 position = new Vector3(VertexInfos[i, j + 1].Position.X, 0, VertexInfos[i, j + 1].Position.Z);
                        switch (direction)
                        {
                            case EDirection.North:
                                position += NoMove;
                                if (i + 2 <= width) LeftTop = VertexInfos[i + 2, j].Position; else Debug.Assert(false);
                                if (i + 2 <= width) RightTop = VertexInfos[i + 2, j + 1].Position; else Debug.Assert(false);
                                LeftMiddle = VertexInfos[i + 1, j].Position;
                                RightMiddle = VertexInfos[i + 1, j + 1].Position;
                                break;
                            case EDirection.South:
                                position += MoveDown;
                                if (i - 1 >= 0) LeftBottom = VertexInfos[i - 1, j].Position; else Debug.Assert(false);
                                if (i - 1 >= 0) RightBottom = VertexInfos[i - 1, j + 1].Position; else Debug.Assert(false);
                                LeftMiddle = VertexInfos[i, j].Position;
                                RightMiddle = VertexInfos[i, j + 1].Position;
                                break;
                            case EDirection.West:
                                position += NoMove;
                                if (j - 1 >= 0) LeftTop = VertexInfos[i + 1, j - 1].Position; else Debug.Assert(false);
                                if (j - 1 >= 0) LeftBottom = VertexInfos[i, j - 1].Position; else Debug.Assert(false);
                                LeftMiddle = VertexInfos[i + 1, j].Position;
                                RightMiddle = VertexInfos[i, j].Position;
                                break;
                            case EDirection.East:
                                position += MoveRight;
                                if (j + 2 <= width) RightTop = VertexInfos[i + 1, j + 2].Position; else Debug.Assert(false);
                                if (j + 2 <= width) RightBottom = VertexInfos[i, j + 2].Position; else Debug.Assert(false);
                                LeftMiddle = VertexInfos[i + 1, j + 1].Position;
                                RightMiddle = VertexInfos[i, j + 1].Position;
                                break;
                        }

                        position.Y = Math.Min(Math.Min(VertexInfos[i, j].Position.Y, VertexInfos[i + 1, j].Position.Y),
                                              Math.Min(VertexInfos[i, j + 1].Position.Y, VertexInfos[i + 1, j + 1].Position.Y));

                        // rotation & states
                        Vector3 rotation = new Vector3(0, -(float)Math.PI / 2.0f, -(float)Math.PI / 2.0f);
                        ERenderState states = ERenderState.Opaque | ERenderState.Solid | ERenderState.CullCounterClockWise | ERenderState.EnableDepth;

                        Texture2D cliffTexture = (VertexInfos[i, j].CliffTextureType == 1) ? TextureManager.Instance.Cliff0 : TextureManager.Instance.Cliff1;
                        int textureId = (VertexInfos[i, j].CliffTextureType == 1) ? 0 : 3;
                        //PaletteInstance instance = new PaletteInstance(

                        //                                              position, rotation, states, manager.GetPaletteRenderer(EPaletteRendererType.Cliff, name));
                        PaletteInstance instance = new CliffInstance(LeftBottom, LeftTop, RightTop, RightBottom, LeftMiddle, RightMiddle, true, direction, side,
                                      cliffTexture, position, rotation, states, manager.GetPaletteRenderer(EPaletteRendererType.Cliff, name) as CliffRenderer);
                        SceneManager.Instance.AddInstance(instance);

                        // Cliff changes tile texture around
                        VertexInfos[i, j].TextureId = VertexInfos[i + 1, j].TextureId =
                        VertexInfos[i, j + 1].TextureId = VertexInfos[i + 1, j + 1].TextureId = (byte)textureId;

                        switch (direction)
                        {
                            case EDirection.North:
                                VertexInfos[i + 2, j].TextureId = VertexInfos[i + 2, j + 1].TextureId = (byte)textureId;
                                break;
                            case EDirection.South:
                                VertexInfos[i - 1, j].TextureId = VertexInfos[i - 1, j + 1].TextureId = (byte)textureId;
                                break;
                            case EDirection.West:
                                VertexInfos[i, j - 1].TextureId = VertexInfos[i + 1, j - 1].TextureId = (byte)textureId;
                                break;
                            case EDirection.East:
                                VertexInfos[i + 1, j + 2].TextureId = VertexInfos[i + 1, j + 2].TextureId = (byte)textureId;
                                break;
                        }

                        // Debug information
                        //DebugPrimitiveRenderer.AddPermanentPrimitive(VertexInfos[i, j].Position, Color.Red);
                        //DebugPrimitiveRenderer.AddPermanentPrimitive(VertexInfos[i + 1, j].Position, Color.Green);
                        //DebugPrimitiveRenderer.AddPermanentPrimitive(VertexInfos[i, j + 1].Position, Color.Blue);
                    }
                }
        }

        private void CalculateRampsHeight()
        {
            const int IterationTimes = 5;
            for (int times = 0; times < IterationTimes; times++)
            {
                for (int x = 1; x < this.Landscape.WarCraftEnvironment.Width - 1; x++)
                    for (int y = 1; y < this.Landscape.WarCraftEnvironment.Height - 1; y++)
                        if (this.Landscape.WarCraftEnvironment.VertexInfos[Index(x, y)].IsRampPoint)
                        {
                            float allX, allY, countX, countY;
                            allX = countX = 0;
                            // X direction
                            for (int dx = -1; dx < 2; dx++)
                                if ((dx != 0 && this.Landscape.WarCraftEnvironment.VertexInfos[Index(x + dx, y)].IsRampPoint))
                                {
                                    allX += this.Landscape.WarCraftEnvironment.VertexInfos[Index(x + dx, y)].Position.Y;
                                    countX++;
                                }

                            allY = countY = 0;
                            // Y direction
                            for (int dy = -1; dy < 2; dy++)
                                if ((dy != 0 && this.Landscape.WarCraftEnvironment.VertexInfos[Index(x, y + dy)].IsRampPoint))
                                {
                                    allY += this.Landscape.WarCraftEnvironment.VertexInfos[Index(x, y + dy)].Position.Y;
                                    countY++;
                                }

                            if (countX + countY == 4)
                                this.Landscape.WarCraftEnvironment.VertexInfos[Index(x, y)].Position.Y = (allX + allY) / 4.0f;
                            else
                            {
                                if (countX == 2)
                                    this.Landscape.WarCraftEnvironment.VertexInfos[Index(x, y)].Position.Y = allX / 2;
                                if (countY == 2)
                                    this.Landscape.WarCraftEnvironment.VertexInfos[Index(x, y)].Position.Y = allY / 2;
                            }
                        }
            }
        }
        private VertexPositionNormalTextureFogBones GenerateTerrainVertex(int x, int y)
        {
            VertexPositionNormalTextureFogBones terrainVertex = new VertexPositionNormalTextureFogBones();

            // get position
            terrainVertex.Position = this.Landscape.WarCraftEnvironment.VertexInfos[Index(x, y)].Position;
            //if (this.Landscape.WarCraftEnvironment.VertexInfos[Index(x, y)].IsRampPoint)
            //    DebugPrimitiveRenderer.AddPermanentPrimitive(terrainVertex.Position + new Vector3(0, 0, 0), Color.Yellow);

            // calculate normal
            // shape
            // 6-->7-->8               7 is (x - 1, y)
            // |   |   |
            // 3-->4-->5               4 is (x, y)
            // |   |   |
            // 0-->1-->2               1 is (x - 1, y)
            Vector3[] position = new Vector3[9];
            bool[] valid = new bool[9];
            for (int i = -1; i <= 1; i++)
                for (int j = -1; j <= 1; j++)
                {
                    int index = (j + 1) + (i + 1) * 3;
                    if ((0 <= x + i) && (x + i <= this.Landscape.WarCraftEnvironment.Width - 1) && (0 <= y + j) && (y + j <= this.Landscape.WarCraftEnvironment.Height - 1))
                    {
                        valid[index] = true;
                        position[index] = this.Landscape.WarCraftEnvironment.VertexInfos[Index(x + i, y + j)].Position;
                    }
                    else
                    {
                        valid[index] = false;
                        position[index] = new Vector3(0, 0, 0);
                    }
                }
            // Triangles
            // 0 - 1 - 4
            // 0 - 4 - 3
            // 1 - 5 - 4
            // 3 - 4 - 7
            // 4 - 5 - 8
            // 4 - 8 - 7
            // get cross values
            int[,] indices = new int[6, 3] { { 0, 3, 4 }, { 0, 4, 1 }, { 1, 4, 5 }, { 3, 7, 4 }, { 4, 7, 8 }, { 4, 8, 5 } };
            terrainVertex.Normal = new Vector3(0, 0, 0);
            for (int i = 0; i < 6; i++)
            {
                // pass the invalid situation
                for (int j = 0; j < 3; j++)
                    if (!valid[indices[i, j]])
                        continue;
                // put point 4 at the middle of array
                while (indices[i, 1] != 4)
                {
                    int t = indices[i, 0]; indices[i, 0] = indices[i, 1]; indices[i, 1] = indices[i, 2]; indices[i, 2] = t;
                }
                // get edge 1 and edge 2, then cross it
                terrainVertex.Normal += Vector3.Cross(position[indices[i, 1]] - position[indices[i, 0]], position[indices[i, 1]] - position[indices[i, 2]]);
            }

            terrainVertex.Fog = 1.0f;
            terrainVertex.Index = Vector4.Zero;
            // tanget and normal are dropped
            //// generate tanget
            //if (valid[5])
            //    terrainVertex.Tanget = position[5] - position[4];
            //else
            //    terrainVertex.Tanget = position[4] - position[3];
            //terrainVertex.Tanget.Normalize();

            //// generate binormal
            //terrainVertex.Binormal = Vector3.Cross(terrainVertex.Tanget, terrainVertex.Normal);

            return terrainVertex;
        }

        private int Index(int x, int y)
        {
            return x + y * (this.Landscape.WarCraftEnvironment.Width);
        }

        public void Draw(GameTime gameTime)
        {
            //Matrix world = Matrix.Identity;
            //Camera camera = this.Landscape.GameObject.GetComponent<Camera>();
            //switch (this.Landscape.GameObject.GameScene.Mode)
            //{
            //    case SceneMode.DebugMode:
            //        camera = this.Landscape.GameObject.GameScene.Debug.GetComponent<DebugCamera>();
            //        break;
            //    case SceneMode.NormalMode:
            //        if (camera == null)
            //            camera = this.Landscape.GameObject.GameScene.Configuration.GetComponent<Camera>();
            //        break;
            //}
            //Matrix view = camera.View;
            //Matrix projection = camera.Projection;
            //for (int i = 0; i < _LandscapePatchRenderers.Count; i++)
            //    _LandscapePatchRenderers[i].Draw(world, view, projection);
        }

        public VertexInfo this[int i, int j]
        {
            get
            {
                return this.VertexInfos[this.Index(i, j)];
            }
        }

        public const int GridSize = 128;

        public int Version;
        public char TextureSet;
        public bool UseCustomTexture;
        public string[] TextureNames;
        public string[] CliffNames;
        public int Width;
        public int Height;
        public float CenterOffsetX;
        public float CenterOffsetY;

        public VertexInfo[] VertexInfos;
        private float[,] _LightInfo;
        private float[,] _TargetLightInfo;
    }
}
